﻿using ShadowCode.Injection.Injections;
using System.CodeDom.Compiler;
using System.Text;

namespace ShadowCode.Injection;
[Generator]
public class InjectionGenerator : Base.GeneratorBase
{
    protected override IncrementalValuesProvider<TransformData> GetCollects
        (IncrementalGeneratorInitializationContext context) =>
        context.SyntaxProvider.CreateSyntaxProvider(IsTarget, Transform);

    private static bool IsTarget(SyntaxNode node, CancellationToken token)
    {
        if (node is not AttributeSyntax attr) return false;
        return GetName(attr.Name) is "AddService" or "AddServiceAttribute";
    }

    protected override void RegisterPostInitializationOutput(
    IncrementalGeneratorPostInitializationContext context) =>
    context.AddSource($"ShadowCode.Injection.ServiceAttribute.cs", $$"""
    /* {{version}} */
    #pragma warning disable
    using Microsoft.Extensions.DependencyInjection;

    namespace ShadowCode;

    internal abstract partial class AddServiceBase : Attribute
    {
        public object? Keyed { get; set; }

        public bool TryAdd { get; set; }

        /// <summary>
        /// 提供实现的工厂方法
        /// </summary>
        public string? Factory { get; set; }

        /// <summary>
        /// 注册顺序 (值越大，顺序越靠后)
        /// </summary>
        public int Index { get; set; }
    }

    /// <summary>
    /// 添加服务 (ShadowCode.Injection)
    /// </summary>
    /// <param name="lifetime">生命周期</param>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor, AllowMultiple = true)]
    internal partial class AddServiceAttribute(ServiceLifetime lifetime) : AddServiceBase
    {
        public Type ServiceType { get; set; } = null!;
    }

    /// <summary>
    /// 添加服务 (ShadowCode.Injection生成)
    /// </summary>
    /// <param name="lifetime">生命周期</param>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor, AllowMultiple = true)]
    internal partial class AddServiceAttribute<TService>(ServiceLifetime lifetime)
        : AddServiceBase
    { }  
    """);

    protected override void InitConfigCollect(StringBuilder config)
    {
        config.AppendLine($"project_dir : {project_dir}")
            .AppendLine($"root_namespace : {root_namespace}")
            .AppendLine($"class_name : {class_name}")
            .AppendLine($"use_method_name : {use_method_name}");
    }

    protected override void Build(SourceProductionContext context,
        Compilation compilation, ImmutableArray<TransformData> array, DateTime start)
    {
        var items = array.Select(x => new InjectionAttribute(x))
            .GroupBy(x => x.GroupKey);

        var sb = Writer(writer =>
        {
            WriteClassStart(writer);
            writer.WriteScope(() => WriteClassBody(writer, items, context));
        });

        var diff = (DateTime.Now - start).TotalMilliseconds;
        sb.Insert(0, $"/* [{index}] {version} {diff:0}ms */\r\n");
        var code = sb.ToString();
        context.AddSource($"ShadowCode.Injection.Extension.cs", code);
    }

    private void WriteClassBody(IndentedTextWriter writer,
        IEnumerable<IGrouping<string, InjectionAttribute>> groups,
        SourceProductionContext context)
    {
        writer.WriteLine("/// <summary> 注册服务 </summary>");
        var method = $"public static IServiceCollection {use_method_name}(this {parameter})";
        writer.WriteLine(method);
        writer.WriteScope(() => WriteMethodBody(writer, groups, context));

        writer.WriteLine();

        writer.WriteLine(generatorSummary);
        writer.WriteLine("[Obsolete(\"由生成器调用，请勿使用！\")]");
        writer.WriteLine($"static partial void g_configure({parameter});");
        writer.WriteLine();

        writer.WriteLine(userusageSummary);
        writer.WriteLine($"static partial void UseStart({parameter});");
        writer.WriteLine();

        writer.WriteLine(userusageSummary);
        writer.WriteLine($"static partial void UseEnd({parameter});");

        writer.WriteLine();
    }

    private void WriteMethodBody(IndentedTextWriter writer,
        IEnumerable<IGrouping<string, InjectionAttribute>> groups,
        SourceProductionContext context)
    {
        writer.WriteLine($"UseStart(service, config);");
        foreach (var group in groups)
        {
            var items = group.OrderBy(x => x.Index).ToArray();
            writer.WriteLine($"#region {group.Key} [{items.Length}]");
            foreach (var item in items) Buid(writer, item, context);
            writer.WriteLine($"#endregion");
        }
        writer.WriteLine($"g_configure(service, config);");
        writer.WriteLine($"UseEnd(service, config);");
        writer.WriteLine("return service;");
    }

    private void Buid(IndentedTextWriter writer,
        InjectionAttribute data, 
        SourceProductionContext context)
    {
        try
        {
            data.Buid(writer);
        }
        catch (Error e)
        {
            context.ReportDiagnostic(e.Diagnostic);
            writer.WriteLine("#warning 生成时发生异常");
            writer.WriteLine($"/* \r\n{e.Message}\r\n */");
        }
        catch (Exception ex)
        {
            writer.WriteLine("#warning 生成时发生异常");
            writer.WriteLine($"/* \r\n{ex}\r\n */");
        }
    }
}